로딩 중이에요... 🐣
26 DB성능 최적화 | ✅ 저자: 이유정(박사)
데이터베이스 성능 최적화란?
데이터베이스가 더 빠르게, 효율적으로 작동하도록 돕는 방법들을 말합니다.
즉, "필요한 데이터를 빠르게 찾고, 저장 공간도 아끼고, 시스템을 안정적으로 유지"하기 위한 다양한 기술입니다.
DB성능종류:
- Query 최적화: 필요한 데이터만 정확히 뽑아야 속도가 빨라요
- 인덱싱: 자주 찾는 열에 책갈피를 달면 조회가 빨라져요
- 데이터 베이스 설계: 너무 잘게 쪼개거나 뭉치면 오히려 느려져요
- 파티셔닝: 테이블을 나누면 필요한 부분만 빨리 읽을 수 있어요
- 하드웨어 서버 최적화: 서버 성능도 중요하지만, 설계가 먼저예요
1️⃣ Query 최적화
SELECT *
처럼 모든 데이터를 가져오는 방식은 느릴 수 있어요.- 정확히 필요한 컬럼만 골라서 조회하는 것이 훨씬 빠릅니다.
-- 나쁨 (느림)
SELECT * FROM employees;
-- 좋음 (빠름)
SELECT name, salary FROM employees WHERE department = 'Sales';
실습코드:
import pandas as pd
import sqlite3
# 1. 예시용 데이터프레임 만들기 (employees 테이블처럼)
data = {
'id': [1, 2, 3, 4, 5],
'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'salary': [5000, 6000, 4500, 7000, 4000],
'department': ['HR', 'Sales', 'HR', 'IT', 'Sales']
}
df = pd.DataFrame(data)
# 2. 메모리 기반 SQLite DB에 저장
conn = sqlite3.connect(":memory:")
df.to_sql("employees", conn, index=False)
# 3. 나쁨 (비효율적인 전체 조회)
sql_all = """
SELECT * FROM employees;
"""
df_all = pd.read_sql(sql_all, conn)
print(" 전체 조회 결과:")
print(df_all)
# 4. 좋음 (필요한 열만 조건 필터링)
sql_filtered = """
SELECT name, salary
FROM employees
WHERE department = 'Sales';
"""
df_filtered = pd.read_sql(sql_filtered, conn)
print("\n 필터링된 조회 결과 (Sales 부서):")
print(df_filtered)
conn.close()
출력결과:
전체 조회 결과:
id name salary department
0 1 Alice 5000 HR
1 2 Bob 6000 Sales
2 3 Charlie 4500 HR
3 4 David 7000 IT
4 5 Eve 4000 Sales
필터링된 조회 결과 (Sales 부서):
name salary
0 Bob 6000
1 Eve 4000
2️⃣ 인덱싱(Indexing)
인덱스는 책의 목차처럼, 데이터가 어디 있는지 빠르게 찾게 도와주는 구조입니다. 자주 검색하는 열(예: id
, email
, salary
)에 인덱스를 걸면 조회 속도가 빨라집니다.
CREATE INDEX idx_salary ON employees(salary);
하지만 인덱스가 너무 많으면 저장 공간이 늘어나고, 데이터를 추가/수정할 때 속도가 느려질 수 있어요. 필요한 곳에만 인덱스를 추가하는 게 중요합니다.
자주 사용되는 인덱스 종류 6가지
1.
B-트리 인덱스 (B-Tree Index)
- 가장 일반적인 인덱스
- 값이 정렬된 상태로 저장되어 있어 범위 검색, 정렬, 비교 연산에 강함
- 대부분의 DB에서 기본 인덱스 타입으로 사용됨
예:
salary > 5000
,name BETWEEN 'A' AND 'M'
2.
해시 인덱스 (Hash Index)
- 값을 해시 함수로 변환하여 저장
- 정확히 일치하는 값을 빠르게 찾을 때 유리함 (
=
조건) - 하지만 범위 검색에는 적합하지 않음
예:
WHERE email = 'user@example.com'
3.
비트맵 인덱스 (Bitmap Index)
- 각 값마다 비트 벡터를 만들어 저장
- 값의 종류가 적고 고정된 경우에 유리 (예: 성별, 국가코드)
- 빠르게 조합 조건을 계산할 수 있음
예:
WHERE gender = 'F' AND region = 'Asia'
4.
전문 검색 인덱스 (Full-Text Index)
- 문장이나 텍스트 데이터에 사용
- 단어 기준으로 분해(indexing)해서 빠르게 검색 가능
LIKE '%word%'
보다 훨씬 빠름 예:WHERE MATCH(content) AGAINST('database')
5.
공간 인덱스 (Spatial Index)
- 좌표나 지도 같은 공간 데이터를 인덱싱
- GIS(지리 정보 시스템)에서 사용됨
- 2D/3D 범위, 거리 기반 쿼리에 강함
예:
WHERE ST_Distance(location, point) < 10
6.
복합 인덱스 (Composite Index)
- 여러 열을 조합해서 만든 인덱스
- 특정 열 조합이 자주 함께 검색될 때 유리
- 왼쪽부터 순서대로 인덱스가 적용됨 (Left-most prefix rule)
예:
CREATE INDEX idx_user_email ON users(username, email);
실습코드:
import pandas as pd
import sqlite3
# 1. 예제 데이터프레임 생성
df = pd.DataFrame({
'id': [1, 2, 3, 4, 5],
'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'salary': [5000, 6000, 4500, 7000, 4000],
'department': ['HR', 'Sales', 'HR', 'IT', 'Sales']
})
# -------------------------------
# SQL 방식: 인덱스 생성
# -------------------------------
conn = sqlite3.connect(":memory:") # 메모리 기반 임시 DB
df.to_sql("employees", conn, index=False)
# SQL에서 salary 열에 인덱스 생성
sql_create_index = """
CREATE INDEX idx_salary ON employees(salary);
"""
conn.execute(sql_create_index)
# 조건 검색 (인덱스가 사용될 수 있음)
sql_query = """
SELECT name, salary
FROM employees
WHERE salary > 5000;
"""
df_sql_result = pd.read_sql(sql_query, conn)
print(" SQL 인덱싱 + 조건 검색 결과:")
print(df_sql_result)
# -------------------------------
# Pandas 방식: 인덱스 설정
# -------------------------------
# salary 열을 인덱스로 설정
df_pandas_indexed = df.set_index('salary')
# 조건 필터링 (salary > 5000)
df_pandas_filtered = df[df['salary'] > 5000][['name', 'salary']]
print("\n Pandas 조건 검색 결과:")
print(df_pandas_filtered)
conn.close()
출력결과:
SQL 인덱싱 + 조건 검색 결과:
name salary
0 Bob 6000
1 David 7000
Pandas 조건 검색 결과:
name salary
1 Bob 6000
3 David 7000
3️⃣ 데이터베이스 설계 - 데이터를 어떻게 나눠서 저장하느냐(= 설계)가 성능에 큰 영향을 줍니다. - 너무 잘게 나누면 느려지고, 너무 뭉치면 관리가 어렵습니다. - 정규화(중복 제거)와 비정규화(속도 개선) 사이에서 균형이 중요합니다.
4️⃣ 파티셔닝(Partitioning)
- 파티셔닝은 큰 테이블을 여러 개로 나누는 기술입니다.
- 예를 들어, sales
테이블을 연도(year)
기준으로 나누면,
특정 연도만 검색할 때 더 빠르게 찾을 수 있습니다.
CREATE TABLE sales
PARTITION BY RANGE (year) (
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024)
);
SQL과 Pandas에서 연도(year) 기준으로 데이터를 나누어
특정 연도에 해당하는 데이터만 빠르게 조회하는 구조를 비교해보기:
import pandas as pd
import sqlite3
# 1. 예제 DataFrame 생성 (sales 데이터)
df = pd.DataFrame({
'id': [1, 2, 3, 4, 5, 6],
'year': [2022, 2022, 2023, 2023, 2024, 2024],
'amount': [100, 150, 200, 250, 300, 350]
})
# 2. SQLite 메모리 DB 생성 및 테이블 저장
conn = sqlite3.connect(":memory:")
df.to_sql("sales", conn, index=False)
# 3. SQL로 특정 연도만 조회 (2023년 데이터)
sql_query = """
SELECT *
FROM sales
WHERE year = 2023;
"""
df_sql_result = pd.read_sql(sql_query, conn)
print(" SQL 방식 - 특정 연도 필터링 결과 (2023년):")
print(df_sql_result)
Pandas방식
# 4. Pandas에서 연도 기준으로 분리
df_2022 = df[df['year'] == 2022]
df_2023 = df[df['year'] == 2023]
df_2024 = df[df['year'] == 2024]
print("\n Pandas 방식 - 2023년 데이터:")
print(df_2023)
# 5. 연도별 데이터프레임을 파티션처럼 저장
partitions = {year: df[df['year'] == year] for year in df['year'].unique()}
# 2023년 파티션 조회
print("\n Pandas 딕셔너리 방식 - 2023 파티션:")
print(partitions[2023])
5️⃣ 🖥 하드웨어 서버 최적화
- 하드디스크 대신 SSD를 쓰거나, 메모리나 CPU를 늘리면 성능이 좋아질 수 있어요.
- 하지만 하드웨어보다 먼저 쿼리와 설계 최적화가 우선입니다.